/**************************************************************************
 * @file serial_protocol.c
 * @author BryanHe(贺璧) (532352406@qq.com)
 * @brief 
 * @version 0.1
 * @date 2025-04-08
 * 
 * @copyright Copyright (c) 2025
 * 
 *************************************************************************/
#include "../inc/serial_protocol.h"


/**************************************************************************
 * @brief 
 * 
 * @param buf 
 * @param len 
 *************************************************************************/
void print_buffer_raw(const uint8_t *buf, uint32_t len) {
    for (uint32_t i = 0; i < len; i++) {
        SP_LOG("%02X ", buf[i]);
    }
    SP_LOG("\n");
}
/**************************************************************************
 * @brief Init the serial protocol handler with user's  
 * 
 * @param handler: 指向串行协议解析句柄
 * @param p_cmd_table：指向用户自定义的指令表，当底层接收完一包数据，完成数据提取和CRC校验后查询
                这个指令表，并调用指令对应的回调函数，用户的事务处理可在这个回调函数中实现，
 * @param cmd_table_size：指令表的成员大小
 * @param s_send_handle：指向用户的串行发送数据函数，这个函数由用户自行实现
 *************************************************************************/
void serial_protocol_init(ProtocolHandler *handler,const UserCommand *p_cmd_table,uint16_t cmd_table_size,serial_send s_send_handle,uint32_t tik_interval){

    memset(handler,0,sizeof(ProtocolHandler));

    handler->p_cmd_table = p_cmd_table;
    handler->cmd_table_size = cmd_table_size;
    handler->s_send_handle = s_send_handle;
    handler->tick_interval = tik_interval;

}

/**************************************************************************
 * @brief 数据发送，用户可调用如下的API接口发送数据到外设，
 * 
 * @param handler 指向串行协议解析句柄
 * @param cmd_buf 指向用户发送数据的缓存，缓存中的数据由用户自行组织
 * @param cmd_len 发送数据的缓存大小
 * @param ack_cb  应答回调函数，发送数据的完成情况的状态反馈，如外设的应答信息等
 *************************************************************************/
void serial_protocol_send(ProtocolHandler *handler, uint8_t *cmd_buf, uint8_t cmd_len,serial_ack_cb ack_cb) {

    int pkg_len;

    pkg_len = pack_data(cmd_buf, cmd_len, handler->tx_buffer);
    handler->last_packet_len = pkg_len;
    handler->last_send_cmd = cmd_buf[0];

    if (pkg_len > 0) {
        if(handler->s_send_handle){
            handler->s_send_handle(handler->tx_buffer, pkg_len);
        }
        handler->ack_status_cb = ack_cb;
        handler->serial_p_tick = 0;
        handler->last_send_time = handler->serial_p_tick;
        handler->waiting_ack = 1;
        handler->retry_count = 0;
    }
}

static void serial_protocol_wait_ack_handle(ProtocolHandler *handler) {
    if (handler->waiting_ack && (handler->serial_p_tick - handler->last_send_time) > (ACK_TIMEOUT_MS/handler->tick_interval)) {
        handler->retry_count++;

        if (handler->retry_count <= MAX_RETRY_TIMES) {
            if(handler->s_send_handle){
                handler->s_send_handle(handler->tx_buffer, handler->last_packet_len);
            }
            handler->last_send_time = handler->serial_p_tick;
        } else {
            if (handler->ack_status_cb != NULL) {
                handler->ack_status_cb(handler->last_send_cmd, STATUS_WAIT_ACK_TIMEOUT, NULL, 0);
            }
            handler->waiting_ack = 0;
        }
    }
}
/**************************************************************************
 * @brief 
 * 
 * @param handler 指向串行协议解析句柄
 * @param cmd 
 * @param status 
 * @param response 
 * @param res_len 
 *************************************************************************/
static void serial_protocol_response(ProtocolHandler *handler, CommandCode cmd, CommandStatus status, uint8_t *response, uint8_t res_len) {
    uint8_t dat[USR_D_PACK_MAX_DAT_PAYLOAD];
    
    int i = 0;
    int pkg_len;

    dat[i++] = CMD_ACK_INFO;
    dat[i++] = cmd;
    dat[i++] = status;
    // dat[i++] = res_len;/**Response data length*/
    if (res_len) {
        memcpy(&dat[i], response, res_len);
        i += res_len;
    }

    pkg_len = pack_data(dat, i, handler->tx_buffer);
    handler->last_packet_len = pkg_len;

    if (pkg_len > 0 && handler->s_send_handle) {
        handler->s_send_handle(handler->tx_buffer, pkg_len);
    }
}
/**************************************************************************
 * @brief 
 * 
 * @param handler 指向串行协议解析句柄
 * @param p_pkg 
 * @param pkg_len 
 *************************************************************************/
static void serial_protocol_rec_handle(ProtocolHandler *handler, const uint8_t *p_pkg, uint16_t pkg_len) {
    uint16_t crc;
    uint8_t dat[USR_D_PACK_MAX_DAT_PAYLOAD];
    uint8_t response[USR_D_PACK_MAX_DAT_PAYLOAD];
    uint16_t res_len;
    uint8_t len = 0;
    CommandCode cmd;
    CommandStatus state;
    uint8_t *param;
    uint8_t para_len;
    uint8_t ack=2;


    SP_LOG("pak_data:\n");
    SP_LOG_RAW(p_pkg,pkg_len);
    // 数据解包函数
    if (0 == unpack_data(p_pkg, pkg_len, dat, &len, &crc)) {
        SP_LOG("usr_unpack_data:\n");
        SP_LOG_RAW(dat,len);
        for (uint8_t i = 0; i < handler->cmd_table_size; i++) {

            cmd = (CommandCode)dat[0];
            para_len = len-1;
            param = &dat[1];

            if(cmd == CMD_ACK_INFO){
                handler->waiting_ack = 0;
                handler->ack_status_cb(param[0],param[1],&param[2],para_len-2);
                ack = 0;
                break;
            }
            else if (handler->p_cmd_table[i].code == cmd) {
                if(handler->p_cmd_table[i].handler != NULL){
                    state = handler->p_cmd_table[i].handler(param, para_len, response, &res_len);
                }else{
                    state = STATUS_SUCCESS;
                }
                
                ack = 1;
                break;
            }
        }

        if(ack==1)serial_protocol_response(handler, cmd, state, response, res_len);
        else if(ack==2) serial_protocol_response(handler, CMD_UNKNOWN, STATUS_INVALID_CMD, NULL, 0);
        else{
            SP_LOG("is a ACK pakg\n");
        }

    } else {
        serial_protocol_response(handler, CMD_UNKNOWN, STATUS_CRC_ERROR, NULL, 0);
    }
}

/**************************************************************************
 * @brief Should be invoked in 1mS loop, 
 * 
 * @param handler 指向串行协议解析句柄
 *************************************************************************/
void serial_protocol_processing(ProtocolHandler *handler){
    handler->serial_p_tick++;

    if(handler->rx_pkg_len){
        serial_protocol_rec_handle(handler,handler->rx_process_buf,handler->rx_pkg_len);
        handler->rx_pkg_len=0;
    }
    serial_protocol_wait_ack_handle(handler);
}

/**************************************************************************
 * @brief 在UART/I2C/SPI接收到数据后调用这个函数，往其中放入接收到的串行数据
 *
 * @param handler 指向串行协议解析句柄
 * @param p_buf   指向串行数据接收缓存
 * @param len     串行数据缓存中的数据长度
 *************************************************************************/
void serial_protocol_receive(ProtocolHandler *handler, uint8_t *p_buf, uint16_t len) {
    for (uint32_t i = 0; i < len; i++) {

        if (handler->rx_index == 0 && p_buf[i] != USR_D_PKG_HEAD) continue;
        if(p_buf[i] == USR_D_PKG_HEAD)handler->rx_index = 0;

        handler->rx_buffer[handler->rx_index++] = p_buf[i];

        if (p_buf[i] == USR_D_PKG_END) {
            memcpy(handler->rx_process_buf, handler->rx_buffer, handler->rx_index);
            handler->rx_pkg_len = handler->rx_index;
            handler->rx_index = 0;
        }

        if (handler->rx_index >= USR_D_PACK_MAX_PACKAGE_LEN) handler->rx_index = 0;

    }
}